package dao

import (
	"encoding/json"
	"errors"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"kf/models/entity"
	"kf/utils"
	"slices"
	"strings"
	"time"
)

type SysMenuDao struct {
	DB  *gorm.DB
	Log *zap.Logger
}

// 根节点ID
var ROOT_NODE_ID uint64 = 0

// 系统默认密码
var DEFAULT_PASSWORD string = "123456"

// 超级管理员角色编码
var ROOT_ROLE_CODE string = "ROOT"

func (sysMenuDao *SysMenuDao) ListRoutes(roles []string) ([]entity.RouteVO, error) {
	var bos = make([]entity.RouteBO, 0)
	if len(roles) == 0 {
		return nil, nil
	}
	condition := ""
	if !slices.Contains(roles, "ROOT") {
		join := strings.Join(roles, ",")
		join = "(" + join + ")"
		condition = "AND t3.code IN " + join
	}

	err2 := sysMenuDao.DB.Raw("SELECT DISTINCT " +
		"t1.id,t1.name,t1.parent_id,t1.route_name,t1.route_path,t1.component,t1.icon,t1.sort," +
		"t1.visible,t1.redirect,t1.type,t1.always_show,t1.keep_alive,t1.params " +
		"FROM sys_menu t1    " +
		"INNER JOIN sys_role_menu t2 ON t1.id = t2.menu_id    " +
		"INNER JOIN sys_role t3 ON t2.role_id = t3.id AND t3.status = 1 AND t3.is_deleted = 0 " +
		"WHERE t1.type != 4 " +
		condition +
		" ORDER BY t1.sort").Scan(&bos).Error
	if err2 != nil {
		return nil, err2
	}
	routes, err := sysMenuDao.BuildRoutes(ROOT_NODE_ID, bos)
	if err != nil {
		return nil, err
	}
	return routes, nil
}

func (sysMenuDao *SysMenuDao) BuildRoutes(parentId uint64, menuList []entity.RouteBO) ([]entity.RouteVO, error) {
	var routeList []entity.RouteVO
	for _, menu := range menuList {
		if menu.ParentId == parentId {
			routeVO, err := sysMenuDao.toRouteVo(menu)
			if err != nil {
				return nil, err
			}
			children, err := sysMenuDao.BuildRoutes(menu.Id, menuList)
			if err != nil {
				return nil, err
			}
			if len(children) > 0 {
				routeVO.Children = children
			}
			routeList = append(routeList, routeVO)
		}
	}
	return routeList, nil
}

// 根据RouteBO创建RouteVO
func (sysMenuDao *SysMenuDao) toRouteVo(routeBO entity.RouteBO) (entity.RouteVO, error) {
	var routeVO entity.RouteVO
	// 获取路由名称
	routeName := routeBO.RouteName
	if routeName == "" {
		// 路由 name 需要驼峰，首字母大写
		routeName = convertToCamelCase(routeBO.RoutePath)

	}
	// 根据name路由跳转 this.$router.push({name:xxx})
	routeVO.Name = routeName
	// 根据path路由跳转 this.$router.push({path:xxx})
	routeVO.Path = routeBO.RoutePath
	routeVO.Redirect = routeBO.Redirect
	routeVO.Component = routeBO.Component

	var meta entity.Meta
	meta.Title = routeBO.Name
	meta.Icon = routeBO.Icon
	meta.Hidden = 0 == routeBO.Visible
	// 【菜单】是否开启页面缓存
	if 1 == routeBO.MenuType && routeBO.KeepAlive == 1 {
		meta.KeepAlive = true
	}
	meta.AlwaysShow = routeBO.AlwaysShow == 1
	params := routeBO.Params
	if params != "" {
		var paramMap []entity.KeyValue
		err := json.Unmarshal([]byte(params), &paramMap)
		if err != nil {
			sysMenuDao.Log.Error("转换报错", zap.Error(err))
			return entity.RouteVO{}, err
		}
		if err != nil {
			return routeVO, err
		}
		meta.Params = paramMap
	}
	routeVO.Meta = meta
	return routeVO, nil
}

// 查询菜单
func (sysMenuDao *SysMenuDao) GetMenus(query entity.MenuQuery) ([]entity.MenuVO, error) {
	tx := sysMenuDao.DB.Model(&entity.SysMenu{})
	var sysMenus []entity.SysMenu
	keywords := query.Keywords
	if keywords != "" {
		tx.Where("name like ?", "%"+keywords+"%")
	}
	err := tx.Find(&sysMenus).Error
	if err != nil {
		return nil, err
	}
	// 获取所有菜单ID
	menuSet := make(utils.Set[uint64])
	for _, sysMenu := range sysMenus {
		menuSet.Add(sysMenu.Id)
	}
	// 获取所有父级ID
	parentSet := make(utils.Set[uint64])
	for _, sysMenu := range sysMenus {
		parentSet.Add(sysMenu.ParentId)
	}
	// 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
	var rootIds []uint64
	for parentId, _ := range parentSet {
		if !menuSet.Contains(parentId) {
			rootIds = append(rootIds, parentId)
		}
	}
	// 使用递归函数来构建菜单树
	var menus []entity.MenuVO
	for _, rootId := range rootIds {
		tree, err := sysMenuDao.BuildMenuTree(rootId, sysMenus)
		if err != nil {
			return nil, err
		}
		menus = append(menus, tree...)
	}
	return menus, nil
}

func (sysMenuDao *SysMenuDao) BuildMenuTree(parentId uint64, menuList []entity.SysMenu) ([]entity.MenuVO, error) {
	var routeList []entity.MenuVO
	for _, menu := range menuList {
		if menu.ParentId == parentId {
			menuVo, err := sysMenuDao.toMenuVo(menu)
			if err != nil {
				return nil, err
			}
			children, err := sysMenuDao.BuildMenuTree(menu.Id, menuList)
			if err != nil {
				return nil, err
			}
			if len(children) > 0 {
				menuVo.Children = children
			}
			routeList = append(routeList, menuVo)
		}
	}
	return routeList, nil
}

func (sysMenuDao *SysMenuDao) toMenuVo(sysMenu entity.SysMenu) (entity.MenuVO, error) {
	var menuVO entity.MenuVO
	// 获取路由名称
	routeName := sysMenu.RouteName
	if routeName == "" {
		// 路由 name 需要驼峰，首字母大写
		routeName = convertToCamelCase(sysMenu.RoutePath)
	}
	menuVO.Component = sysMenu.Component
	menuVO.Icon = sysMenu.Icon
	menuVO.Id = sysMenu.Id
	// 根据name路由跳转 this.$router.push({name:xxx})
	menuVO.Name = sysMenu.Name
	menuVO.ParentId = sysMenu.ParentId
	menuVO.Perm = sysMenu.Perm
	menuVO.Redirect = sysMenu.Redirect
	menuVO.RouteName = sysMenu.RouteName
	menuVO.RoutePath = sysMenu.RoutePath
	menuVO.Sort = sysMenu.Sort
	menuVO.MenuType = MenuTypeEnum[sysMenu.Type]
	menuVO.Visible = sysMenu.Visible
	return menuVO, nil
}

var MenuTypeEnum = map[uint8]string{
	0: "",
	1: "MENU",
	2: "CATALOG",
	3: "EXTLINK",
	4: "BUTTON",
}

func GetValueKeys(m map[uint8]string, value string) uint8 {
	for k, v := range m {
		if v == value {
			return k
		}
	}
	return 0
}

// 菜单下拉列表
func (sysMenuDao *SysMenuDao) ListMenuOptions() ([]utils.Option[uint64], error) {
	var option []utils.Option[uint64]
	var menus []entity.SysMenu
	err := sysMenuDao.DB.Model(&entity.SysMenu{}).Order("sort asc").Find(&menus).Error
	if err != nil {
		return option, err
	}
	option, err = sysMenuDao.BuildMenuOptions(ROOT_NODE_ID, menus)
	return option, err
}

func (sysMenuDao *SysMenuDao) BuildMenuOptions(parentId uint64, menuList []entity.SysMenu) ([]utils.Option[uint64], error) {
	var menuOptions []utils.Option[uint64]
	for _, menu := range menuList {
		if menu.ParentId == parentId {
			option := utils.Option[uint64]{
				Value: menu.Id,
				Label: menu.Name,
			}
			subMenuOptions, _ := sysMenuDao.BuildMenuOptions(menu.Id, menuList)
			if len(subMenuOptions) > 0 {
				option.Children = subMenuOptions
			}
			menuOptions = append(menuOptions, option)
		}
	}
	return menuOptions, nil
}

// 根据id查询
func (sysMenuDao *SysMenuDao) GetMenuById(id uint64) (entity.SysMenu, error) {
	var menu entity.SysMenu
	result := sysMenuDao.DB.Model(&entity.SysMenu{}).Where("id = ?", id).First(&menu)
	if errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return menu, errors.New("菜单不存在")
	} else if result.Error != nil {
		return menu, result.Error
	}
	return menu, nil
}

// 根据id查询菜单信息
func (sysMenuDao *SysMenuDao) GetMenuForm(id uint64) (form entity.MenuForm, err error) {
	menu, err := sysMenuDao.GetMenuById(id)
	if err != nil {
		return entity.MenuForm{}, err
	}
	form = entity.MenuForm{
		Id:         menu.Id,
		ParentId:   menu.ParentId,
		Name:       menu.Name,
		Type:       MenuTypeEnum[menu.Type],
		RouteName:  menu.RouteName,
		RoutePath:  menu.RoutePath,
		Component:  menu.Component,
		Perm:       menu.Perm,
		Visible:    menu.Visible,
		Sort:       menu.Sort,
		Icon:       menu.Icon,
		Redirect:   menu.Redirect,
		KeepAlive:  menu.KeepAlive,
		AlwaysShow: menu.AlwaysShow,
	}
	params := menu.Params
	if params != "" {
		var keyValue []entity.KeyValue
		// 使用json.Unmarshal将JSON字符串解析到切片中
		err := json.Unmarshal([]byte(params), &keyValue)
		if err != nil {
			return form, err
		}
		form.Params = keyValue
	}
	return form, nil
}

func (sysMenuDao *SysMenuDao) SaveMenu(menuForm entity.MenuForm) (bool, error) {
	//菜单类型（1-菜单 2-目录 3-外链 4-按钮）
	menuType := menuForm.Type
	if menuType == MenuTypeEnum[2] { // 如果是目录
		path := menuForm.RoutePath
		if menuForm.ParentId == 0 && !strings.HasPrefix(path, "/") {
			menuForm.RoutePath = "/" + path // 一级目录需以 / 开头
		}
		menuForm.Component = "Layout"
	} else if menuType == MenuTypeEnum[3] { // 如果是目录
		menuForm.Component = ""
	}

	sysMenu := entity.SysMenu{
		Id:         menuForm.Id,
		ParentId:   menuForm.ParentId,
		Name:       menuForm.Name,
		Type:       GetValueKeys(MenuTypeEnum, menuForm.Type),
		RouteName:  menuForm.RouteName,
		RoutePath:  menuForm.RoutePath,
		Component:  menuForm.Component,
		Perm:       menuForm.Perm,
		Visible:    menuForm.Visible,
		Sort:       menuForm.Sort,
		Icon:       menuForm.Icon,
		Redirect:   menuForm.Redirect,
		KeepAlive:  menuForm.KeepAlive,
		AlwaysShow: menuForm.AlwaysShow,
	}

	treePath, err := sysMenuDao.generateMenuTreePath(menuForm.ParentId)
	if err != nil {
		return false, err
	}
	sysMenu.TreePath = treePath
	// 路由参数 [{key:"id",value:"1"}，{key:"name",value:"张三"}] 转换为 [{"id":"1"},{"name":"张三"}]
	params := menuForm.Params
	if params != nil {
		jsonData, err := json.Marshal(params)
		if err != nil {
			return false, err
		}
		sysMenu.Params = string(jsonData)
	} else {
		sysMenu.Params = "[]"
	}

	if sysMenu.Id == 0 {
		err := sysMenuDao.DB.Model(&entity.SysMenu{}).Save(&sysMenu).Error
		if err != nil {
			return false, err
		}
	} else {
		sysMenu.UpdateTime = time.Now()
		err := sysMenuDao.DB.Model(&entity.SysMenu{}).Select("parent_id", "tree_path",
			"name", "type", "route_name", "route_path", "component", "perm", "always_show",
			"keep_alive", "visible", "sort", "icon", "redirect", "update_time", "params").
			Where("id = ?", sysMenu.Id).
			Updates(&sysMenu).Error
		if err != nil {
			return false, err
		}
	}
	return true, nil
}

/**
 * 部门路径生成
 *
 * @param parentId 父ID
 * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
 */
func (sysMenuDao *SysMenuDao) generateMenuTreePath(parentId uint64) (string, error) {
	if ROOT_NODE_ID == parentId {
		return string(parentId), nil
	} else {
		var menu entity.SysMenu
		result := sysMenuDao.DB.Model(&entity.SysMenu{}).Where("id = ?", parentId).First(&menu)
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return "", nil
		} else if result.Error != nil {
			return "", result.Error
		}
		return menu.TreePath + "," + string(menu.Id), nil
	}
}

func (sysMenuDao *SysMenuDao) DeleteMenu(id string) (bool, error) {
	err := sysMenuDao.DB.Where("id = ? OR CONCAT (',',tree_path,',') LIKE CONCAT('%,',?,',%')", id, id).
		Delete(&entity.SysMenu{}).Error
	if err != nil {
		return false, err
	}
	return true, nil
}

// convertToCamelCase 将由连字符分隔的字符串转换为驼峰命名法
func convertToCamelCase(s string) string {
	words := strings.Split(s, "-")
	for i := 1; i < len(words); i++ {
		if len(words[i]) > 0 {
			words[i] = strings.ToUpper(string(words[i][0])) + words[i][1:]
		}
	}
	return strings.Join(words, "")
}

// stringToMap 将格式化的字符串转换为 map
func stringToMap(input, pairSep, keyValueSep string) map[string]string {
	result := make(map[string]string)
	// 分割字符串为键值对
	pairs := strings.Split(input, pairSep)
	for _, pair := range pairs {
		// 分割键值对为键和值
		kv := strings.SplitN(pair, keyValueSep, 2)
		if len(kv) == 2 {
			key := strings.TrimSpace(kv[0])
			value := strings.TrimSpace(kv[1])
			result[key] = value
		}
	}
	return result
}
